#include "drivemap.h"
#include <iostream>
#include <stdlib.h>
#include <rs232.h>
#include <signal.h>

driveMap::driveMap(int numDevice) {
    this->packageOut.address = 0x15;
    this->numOfDevice = numDevice;

// is so ugly
// action with com must be protected in class
    if(OpenComport(numDevice, 9600)) {
        std::cout << "COM#" << numDevice << " port:\tfailed open ):" << std::endl;
        exit(1);
    }
    else std::cout << "COM#" << numDevice << " port:\topen" << std::endl;
}

driveMap::~driveMap() {
    CloseComport(this->numOfDevice);
}


int driveMap::echoTest(int mode) {
// mode = 0 for usualy echo
// mode = 1 for search
    this->packageOut.cmdCode = 0xF0; //f0
    this->packageOut.param1  = 0;
    this->packageOut.param0  = 0;
    this->packageOut.data1   = 0;
    this->packageOut.data0   = 0;
    this->packageOut.status  = 0;

    pkgline pkg = {0, 0};
    this->synthesisPkg(pkg);

    if(!mode) this->sendPkg(pkg);
    else this->sendPkgWithoutGet(pkg);

    return 1;
}

int driveMap::getParametrs() {

}

void driveMap::statLexema() {
    std::cout << std::endl;
    ((this->packageIn.status & 0b1)         != 0) ? this->printMsg("status:\tcurrent portection has worked\0")  : this->printMsg("status:\tcurrent portection has no worked\0");
    ((this->packageIn.status & 0b10)        != 0) ? this->printMsg("status:\tPWMs signal has max value\0")      : this->printMsg("status:\tPWMs signal has not max value\0");
    ((this->packageIn.status & 0b100)       != 0) ? this->printMsg("status:\tdirection - forvard\0")            : this->printMsg("status:\tdirection - reverce\0");
    ((this->packageIn.status & 0b1000)      != 0) ? this->printMsg("status:\tmotor moves\0")                    : this->printMsg("status:\tmotor no moves\0");
    ((this->packageIn.status & 0b10000)     != 0) ? this->printMsg("status:\treverce movie - accept\0")         : this->printMsg("status:\treverce movie not accept\0");
    ((this->packageIn.status & 0b100000)    != 0) ? this->printMsg("status:\tforvard movie - accept\0")         : this->printMsg("status:\tforvard movie not accept\0");
    ((this->packageIn.status & 0b1000000)   != 0) ? this->printMsg("status:\tterminal switch: reserve movie - accept\0") : this->printMsg("status:\tterminal switch: reserve movie not accept\0");
    ((this->packageIn.status & 0b10000000)  != 0) ? this->printMsg("status:\tterminal switch: forvard movie - accept\0") : this->printMsg("status:\tterminal switch: forvard movie not accept\0");
}

int driveMap::execAction() {

    this->packageOut.cmdCode = 0x4B; //f0
    this->packageOut.param1  = 0;
    this->packageOut.param0  = 0;
    this->packageOut.data1   = 0;
    this->packageOut.data0   = 0;
    this->packageOut.status  = 0;

    pkgline pkg = {0, 0};
    this->synthesisPkg(pkg);

    this->sendPkg(pkg);

    return 1;
}

void driveMap::synthesisPkg(pkgline pkg) {
    *pkg |= (unsigned int) this->packageOut.address;
    *pkg |= (unsigned int) this->packageOut.cmdCode << 8;
    *pkg |= (unsigned int) this->packageOut.param1 << 16;
    *pkg |= (unsigned int) this->packageOut.param0 << 24;
    *(pkg + 1) |= (int) this->packageOut.data1;
    *(pkg + 1) |= (int) this->packageOut.data0 << 8;
    *(pkg + 1) |= (unsigned int) this->packageOut.status << 16;
    *(pkg + 1) |= (unsigned int) this->getControlSum(&this->packageOut);
//    *(pkg + 1) |= (unsigned int) this->packageOut.status << 24;
}

int driveMap::parsingPkg(pkgMtx *pkg) {
    this->packageIn.address = (unsigned char) (**pkg[0]);
    this->packageIn.cmdCode = (unsigned char) (**pkg[0] >> 8);
    this->packageIn.param1  = (unsigned char) (**pkg[0] >> 16);
    this->packageIn.param0  = (unsigned char) (**pkg[0] >> 24);
    this->packageIn.data1   = (unsigned char) (**(pkg[1]));
    this->packageIn.data0   = (unsigned char) (**(pkg[1]) >> 8);
    this->packageIn.status  = (unsigned char) (**(pkg[1]) >> 16);
    this->packageIn.cs      = (unsigned char) (**(pkg[1]) >> 24);

    if(this->packageIn.cs == this->getControlSum(&this->packageIn)) return 1;
    return 0;
}

int driveMap::parsing2Pkg(char *buffer) {
    this->packageIn.address = buffer[0];
    this->packageIn.cmdCode = buffer[1];
    this->packageIn.param1  = buffer[2];
    this->packageIn.param0  = buffer[3];
    this->packageIn.data1   = buffer[4];
    this->packageIn.data0   = buffer[5];
    this->packageIn.status  = buffer[6];
    this->packageIn.cs      = buffer[7];
    return 0;
}

int driveMap::sendPkg(pkgline request) {

    unsigned char *buffer = new unsigned char[8];
    unsigned int *tmp0 = (unsigned int*) buffer;
    unsigned int *tmp1 = (unsigned int*) (buffer + 4);
    *tmp0 = request[0];
    *tmp1 = request[1];
//    unsigned long *view = (unsigned long*) buffer;
//    std::cout << "debug: tmp0 = " << std::hex << *tmp0 << ";\ttmp1 = " << *tmp1 << ";\tbuffer = " << buffer << std::hex << ";\t test = " << *test << std::endl;
    std::cout << "COM#" << this->numOfDevice << " port:\t" << "send package: " << buffer << std::endl;

    if(!SendBuf(this->numOfDevice, buffer, 8)) std::cout << "COM#" << this->numOfDevice << " port:\t" << "send fail" << std::endl;
    pkgline *pkg;

    this->getPkg(pkg);

    std::cout << "debug: address = " << std::hex << (int) this->packageIn.address << std::endl;
    return 0;
}

int driveMap::sendPkgWithoutGet(pkgline request) {
    unsigned char *buffer = new unsigned char[8];
    unsigned int *tmp0 = (unsigned int*) buffer;
    unsigned int *tmp1 = (unsigned int*) (buffer + 4);
    *tmp0 = request[0];
    *tmp1 = request[1];
    std::cout << "COM#" << this->numOfDevice << " port:\t" << "send package: " << buffer << std::endl;

    if(!SendBuf(this->numOfDevice, buffer, 8)) std::cout << "COM#" << this->numOfDevice << " port:\t" << "send fail" << std::endl;
}

unsigned char driveMap::getControlSum(package *pkg) {
    return (-1) * abs(pkg->address + pkg->cmdCode + pkg->param1 + pkg->param0 + pkg->data1 + pkg->data0 + pkg->status);
}

void driveMap::printMsg(char *str) {
    std::cout << "COM#" << this->numOfDevice << " port:\t" << str << std::endl;
}

void driveMap::findDevice() {
    unsigned int *tmpAddress = (unsigned int*) &this->packageOut.address;
    int pid = fork();

    if(pid == 0) {
        std::cout << "output of fork process with pid " << pid << std::endl;
//        __sighandler_t *tmp = (void(**)(int)) this->signal4ForkSearch;
        signal(SIGINT, &(driveMap::signal4ForkSearch));
        int i;
        for(*tmpAddress = 0x1, i = 0; i <= 255; (*tmpAddress)++, i++) {
            this->echoTest(1);
            usleep(100000);
        }
        exit(1);

    }else if(pid > 0) {
        this->printMsg("searche device...");
        pkgline *pkg;
        this->getPkg(pkg);

        std::cout << "success. Device number: " << std::hex << (unsigned int) this->packageIn.address <<  std::endl;
        kill(pid, SIGINT);

    }

}

void driveMap::signal4ForkSearch(int sign) {
    exit(0);
}

int driveMap::getPkg(pkgline *pkg) {
    this->printMsg("wait answer from device");

    unsigned char buffer[8];
    while(true) {
        int numBytes = PollComport(this->numOfDevice, buffer, 8);
        if(numBytes == 8) {
            this->printMsg("recerved 8 bytes from device");
            unsigned long *view = (unsigned long*) buffer;
//            std::cout << "debug: answer = " << buffer << " : (long) "<< std::hex << *view << std::endl;
            char *buffer2 = new char[8];
            memcpy(buffer2, (const char*)&buffer, 8);
            this->parsing2Pkg(buffer2);
//            std::cout << "debug: " << std::hex << (int) this->packageIn.address << " -> " << (int) this->packageIn.cmdCode << " -> " << (int) this->packageIn.cs << std::endl;
            return 1;
        }

        usleep(100000);
    }
    return 1;
}
